home *** CD-ROM | disk | FTP | other *** search
/ TeX 1995 July / TeX CD-ROM July 1995 (Disc 1)(Walnut Creek)(1995).ISO / macros / texinfo / info / info-utils.c < prev    next >
C/C++ Source or Header  |  1994-01-28  |  16KB  |  654 lines

  1. /* info-utils.c -- Useful functions for manipulating Info file quirks. */
  2.  
  3. /* This file is part of GNU Info, a program for reading online documentation
  4.    stored in Info format.
  5.  
  6.    Copyright (C) 1993 Free Software Foundation, Inc.
  7.  
  8.    This program is free software; you can redistribute it and/or modify
  9.    it under the terms of the GNU General Public License as published by
  10.    the Free Software Foundation; either version 2, or (at your option)
  11.    any later version.
  12.  
  13.    This program is distributed in the hope that it will be useful,
  14.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  15.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16.    GNU General Public License for more details.
  17.  
  18.    You should have received a copy of the GNU General Public License
  19.    along with this program; if not, write to the Free Software
  20.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  21.  
  22.    Written by Brian Fox (bfox@ai.mit.edu). */
  23.  
  24. #include <stdio.h>        /* For "NULL".  Yechhh! */
  25. #include <ctype.h>
  26. #include <sys/types.h>
  27. #include <sys/stat.h>
  28. #include "info-utils.h"
  29.  
  30. /* When non-zero, various display and input functions handle ISO Latin
  31.    character sets correctly. */
  32. int ISO_Latin_p = 0;
  33.  
  34. /* Variable which holds the most recent filename parsed as a result of
  35.    calling info_parse_xxx (). */
  36. char *info_parsed_filename = (char *)NULL;
  37.  
  38. /* Variable which holds the most recent nodename parsed as a result of
  39.    calling info_parse_xxx (). */
  40. char *info_parsed_nodename = (char *)NULL;
  41.  
  42. /* Functions to remember a filename or nodename for later return. */
  43. static void save_filename (), saven_filename ();
  44. static void save_nodename (), saven_nodename ();
  45.  
  46. /* How to get a reference (either menu or cross). */
  47. static REFERENCE **info_references_internal ();
  48.  
  49. /* Parse the filename and nodename out of STRING.  If STRING doesn't
  50.    contain a filename (i.e., it is NOT (FILENAME)NODENAME) then set
  51.    INFO_PARSED_FILENAME to NULL.  If second argument NEWLINES_OKAY is
  52.    non-zero, it says to allow the nodename specification to cross a
  53.    newline boundary (i.e., only `,', `.', or `TAB' can end the spec). */
  54. void
  55. info_parse_node (string, newlines_okay)
  56.      char *string;
  57.      int newlines_okay;
  58. {
  59.   register int i = 0;
  60.  
  61.   /* Default the answer. */
  62.   save_filename ((char *)NULL);
  63.   save_nodename ((char *)NULL);
  64.  
  65.   /* Special case of nothing passed.  Return nothing. */
  66.   if (!string || !*string)
  67.     return;
  68.  
  69.   string += skip_whitespace (string);
  70.  
  71.   /* Check for (FILENAME)NODENAME. */
  72.   if (*string == '(')
  73.     {
  74.       i = 0;
  75.       /* Advance past the opening paren. */
  76.       string++;
  77.  
  78.       /* Find the closing paren. */
  79.       while (string[i] && string[i] != ')')
  80.     i++;
  81.  
  82.       /* Remember parsed filename. */
  83.       saven_filename (string, i);
  84.  
  85.       /* Point directly at the nodename. */
  86.       string += i;
  87.  
  88.       if (*string)
  89.     string++;
  90.     }
  91.  
  92.   /* Parse out nodename. */
  93.   i = skip_node_characters (string, newlines_okay);
  94.   saven_nodename (string, i);
  95.   canonicalize_whitespace (info_parsed_nodename);
  96.   if (info_parsed_nodename && !*info_parsed_nodename)
  97.     {
  98.       free (info_parsed_nodename);
  99.       info_parsed_nodename = (char *)NULL;
  100.     }
  101. }
  102.  
  103. /* Return the node addressed by LABEL in NODE (usually one of "Prev:",
  104.    "Next:", "Up:", "File:", or "Node:".  After a call to this function,
  105.    the global INFO_PARSED_NODENAME and INFO_PARSED_FILENAME contain
  106.    the information. */
  107. void
  108. info_parse_label (label, node)
  109.      char *label;
  110.      NODE *node;
  111. {
  112.   register int i;
  113.   char *nodeline;
  114.  
  115.   /* Default answer to failure. */
  116.   save_nodename ((char *)NULL);
  117.   save_filename ((char *)NULL);
  118.  
  119.   /* Find the label in the first line of this node. */
  120.   nodeline = node->contents;
  121.   i = string_in_line (label, nodeline);
  122.  
  123.   if (i == -1)
  124.     return;
  125.  
  126.   nodeline += i;
  127.   nodeline += skip_whitespace (nodeline);
  128.   info_parse_node (nodeline, DONT_SKIP_NEWLINES);
  129. }
  130.  
  131. /* **************************************************************** */
  132. /*                                    */
  133. /*            Finding and Building Menus                */
  134. /*                                    */
  135. /* **************************************************************** */
  136.  
  137. /* Return a NULL terminated array of REFERENCE * which represents the menu
  138.    found in NODE.  If there is no menu in NODE, just return a NULL pointer. */
  139. REFERENCE **
  140. info_menu_of_node (node)
  141.      NODE *node;
  142. {
  143.   long position;
  144.   SEARCH_BINDING search;
  145.   REFERENCE **menu = (REFERENCE **)NULL;
  146.  
  147.   search.buffer = node->contents;
  148.   search.start = 0;
  149.   search.end = node->nodelen;
  150.   search.flags = S_FoldCase;
  151.  
  152.   /* Find the start of the menu. */
  153.   position = search_forward (INFO_MENU_LABEL, &search);
  154.  
  155.   if (position == -1)
  156.     return ((REFERENCE **) NULL);
  157.  
  158.   /* We have the start of the menu now.  Glean menu items from the rest
  159.      of the node. */
  160.   search.start = position + strlen (INFO_MENU_LABEL);
  161.   search.start += skip_line (search.buffer + search.start);
  162.   search.start--;
  163.   menu = info_menu_items (&search);
  164.   return (menu);
  165. }
  166.  
  167. /* Return a NULL terminated array of REFERENCE * which represents the cross
  168.    refrences found in NODE.  If there are no cross references in NODE, just
  169.    return a NULL pointer. */
  170. REFERENCE **
  171. info_xrefs_of_node (node)
  172.      NODE *node;
  173. {
  174.   SEARCH_BINDING search;
  175.  
  176.   search.buffer = node->contents;
  177.   search.start = 0;
  178.   search.end = node->nodelen;
  179.   search.flags = S_FoldCase;
  180.  
  181.   return (info_xrefs (&search));
  182. }
  183.  
  184. /* Glean menu entries from BINDING->buffer + BINDING->start until we
  185.    have looked at the entire contents of BINDING.  Return an array
  186.    of REFERENCE * that represents each menu item in this range. */
  187. REFERENCE **
  188. info_menu_items (binding)
  189.      SEARCH_BINDING *binding;
  190. {
  191.   return (info_references_internal (INFO_MENU_ENTRY_LABEL, binding));
  192. }
  193.  
  194. /* Glean cross references from BINDING->buffer + BINDING->start until
  195.    BINDING->end.  Return an array of REFERENCE * that represents each
  196.    cross reference in this range. */
  197. REFERENCE **
  198. info_xrefs (binding)
  199.      SEARCH_BINDING *binding;
  200. {
  201.   return (info_references_internal (INFO_XREF_LABEL, binding));
  202. }
  203.  
  204. /* Glean cross references or menu items from BINDING.  Return an array
  205.    of REFERENCE * that represents the items found. */
  206. static REFERENCE **
  207. info_references_internal (label, binding)
  208.      char *label;
  209.      SEARCH_BINDING *binding;
  210. {
  211.   SEARCH_BINDING search;
  212.   REFERENCE **refs = (REFERENCE **)NULL;
  213.   int refs_index = 0, refs_slots = 0;
  214.   int searching_for_menu_items = 0;
  215.   long position;
  216.  
  217.   search.buffer = binding->buffer;
  218.   search.start = binding->start;
  219.   search.end = binding->end;
  220.   search.flags = S_FoldCase | S_SkipDest;
  221.  
  222.   searching_for_menu_items = (stricmp (label, INFO_MENU_ENTRY_LABEL) == 0);
  223.  
  224.   while ((position = search_forward (label, &search)) != -1)
  225.     {
  226.       int offset, start;
  227.       char *refdef;
  228.       REFERENCE *entry;
  229.  
  230.       search.start = position;
  231.       search.start += skip_whitespace (search.buffer + search.start);
  232.       start = search.start - binding->start;
  233.       refdef = search.buffer + search.start;
  234.       offset = string_in_line (":", refdef);
  235.  
  236.       /* When searching for menu items, if no colon, there is no
  237.      menu item on this line. */
  238.       if (offset == -1)
  239.     {
  240.       if (searching_for_menu_items)
  241.         continue;
  242.       else
  243.         {
  244.           int temp;
  245.  
  246.           temp = skip_line (refdef);
  247.           offset = string_in_line (":", refdef + temp);
  248.           if (offset == -1)
  249.         continue;    /* Give up? */
  250.           else
  251.         offset += temp;
  252.         }
  253.     }
  254.  
  255.       entry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
  256.       entry->filename = (char *)NULL;
  257.       entry->nodename = (char *)NULL;
  258.       entry->label = (char *)xmalloc (offset);
  259.       strncpy (entry->label, refdef, offset - 1);
  260.       entry->label[offset - 1] = '\0';
  261.       canonicalize_whitespace (entry->label);
  262.  
  263.       refdef += offset;
  264.       entry->start = start;
  265.       entry->end = refdef - binding->buffer;
  266.  
  267.       /* If this reference entry continues with another ':' then the
  268.      nodename is the same as the label. */
  269.       if (*refdef == ':')
  270.     {
  271.       entry->nodename = savestring (entry->label);
  272.     }
  273.       else
  274.     {
  275.       /* This entry continues with a specific nodename.  Parse the
  276.          nodename from the specification. */
  277.  
  278.       refdef += skip_whitespace_and_newlines (refdef);
  279.  
  280.       if (searching_for_menu_items)
  281.         info_parse_node (refdef, DONT_SKIP_NEWLINES);
  282.       else
  283.         info_parse_node (refdef, SKIP_NEWLINES);
  284.  
  285.       if (info_parsed_filename)
  286.         entry->filename = savestring (info_parsed_filename);
  287.  
  288.       if (info_parsed_nodename)
  289.         entry->nodename = savestring (info_parsed_nodename);
  290.     }
  291.  
  292.       add_pointer_to_array
  293.     (entry, refs_index, refs, refs_slots, 50, REFERENCE *);
  294.     }
  295.   return (refs);
  296. }
  297.  
  298. /* Get the entry associated with LABEL in MENU.  Return a pointer to the
  299.    REFERENCE if found, or NULL. */
  300. REFERENCE *
  301. info_get_labeled_reference (label, references)
  302.      char *label;
  303.      REFERENCE **references;
  304. {
  305.   register int i;
  306.   REFERENCE *entry;
  307.  
  308.   for (i = 0; references && (entry = references[i]); i++)
  309.     {
  310.       if (strcmp (label, entry->label) == 0)
  311.     return (entry);
  312.     }
  313.   return ((REFERENCE *)NULL);
  314. }
  315.  
  316. /* A utility function for concatenating REFERENCE **.  Returns a new
  317.    REFERENCE ** which is the concatenation of REF1 and REF2.  The REF1
  318.    and REF2 arrays are freed, but their contents are not. */
  319. REFERENCE **
  320. info_concatenate_references (ref1, ref2)
  321.      REFERENCE **ref1, **ref2;
  322. {
  323.   register int i, j;
  324.   REFERENCE **result;
  325.   int size;
  326.  
  327.   /* With one argument passed as NULL, simply return the other arg. */
  328.   if (!ref1)
  329.     return (ref2);
  330.   else if (!ref2)
  331.     return (ref1);
  332.  
  333.   /* Get the total size of the slots that we will need. */
  334.   for (i = 0; ref1[i]; i++);
  335.   size = i;
  336.   for (i = 0; ref2[i]; i++);
  337.   size += i;
  338.  
  339.   result = (REFERENCE **)xmalloc ((1 + size) * sizeof (REFERENCE *));
  340.  
  341.   /* Copy the contents over. */
  342.   for (i = 0; ref1[i]; i++)
  343.     result[i] = ref1[i];
  344.  
  345.   j = i;
  346.   for (i = 0; ref2[i]; i++)
  347.     result[j++] = ref2[i];
  348.  
  349.   result[j] = (REFERENCE *)NULL;
  350.   free (ref1);
  351.   free (ref2);
  352.   return (result);
  353. }
  354.  
  355. /* Free the data associated with REFERENCES. */
  356. void
  357. info_free_references (references)
  358.      REFERENCE **references;
  359. {
  360.   register int i;
  361.   REFERENCE *entry;
  362.  
  363.   if (references)
  364.     {
  365.       for (i = 0; references && (entry = references[i]); i++)
  366.     {
  367.       maybe_free (entry->label);
  368.       maybe_free (entry->filename);
  369.       maybe_free (entry->nodename);
  370.  
  371.       free (entry);
  372.     }
  373.  
  374.       free (references);
  375.     }
  376. }
  377.  
  378. /* Search for sequences of whitespace or newlines in STRING, replacing
  379.    all such sequences with just a single space.  Remove whitespace from
  380.    start and end of string. */
  381. void
  382. canonicalize_whitespace (string)
  383.      char *string;
  384. {
  385.   register int i, j;
  386.   int len, whitespace_found, whitespace_loc;
  387.   char *temp;
  388.  
  389.   if (!string)
  390.     return;
  391.  
  392.   len = strlen (string);
  393.   temp = (char *)xmalloc (1 + len);
  394.  
  395.   /* Search for sequences of whitespace or newlines.  Replace all such
  396.      sequences in the string with just a single space. */
  397.  
  398.   whitespace_found = 0;
  399.   for (i = 0, j = 0; string[i]; i++)
  400.     {
  401.       if (whitespace_or_newline (string[i]))
  402.     {
  403.       whitespace_found++;
  404.       whitespace_loc = i;
  405.       continue;
  406.     }
  407.       else
  408.     {
  409.       if (whitespace_found && whitespace_loc)
  410.         {
  411.           whitespace_found = 0;
  412.           temp[j++] = ' ';
  413.         }
  414.  
  415.       temp[j++] = string[i];
  416.     }
  417.     }
  418.  
  419.   /* Kill trailing whitespace. */
  420.   if (j && whitespace (temp[j - 1]))
  421.     j--;
  422.  
  423.   temp[j] = '\0';
  424.   strcpy (string, temp);
  425.   free (temp);
  426. }
  427.  
  428. /* String representation of a char returned by printed_representation (). */
  429. static char the_rep[10];
  430.  
  431. /* Return a pointer to a string which is the printed representation
  432.    of CHARACTER if it were printed at HPOS. */
  433. char *
  434. printed_representation (character, hpos)
  435.      unsigned char character;
  436.      int hpos;
  437. {
  438.   register int i = 0;
  439.   int printable_limit;
  440.  
  441.   if (ISO_Latin_p)
  442.     printable_limit = 160;
  443.   else
  444.     printable_limit = 127;
  445.  
  446.   if (character == '\177')
  447.     {
  448.       the_rep[i++] = '^';
  449.       the_rep[i++] = '?';
  450.     }
  451.   else if (iscntrl (character))
  452.     {
  453.       switch (character)
  454.     {
  455.     case '\r':
  456.     case '\n':
  457.       the_rep[i++] = character;
  458.       break;
  459.  
  460.     case '\t':
  461.       {
  462.         int tw;
  463.  
  464.         tw = ((hpos + 8) & 0xf8) - hpos;
  465.         while (i < tw)
  466.           the_rep[i++] = ' ';
  467.       }
  468.       break;
  469.  
  470.     default:
  471.       the_rep[i++] = '^';
  472.       the_rep[i++] = (character | 0x40);
  473.     }
  474.     }
  475.   else if (character > printable_limit)
  476.     {
  477.       sprintf (the_rep + i, "\\%0o", character);
  478.       i = strlen (the_rep);
  479.     }
  480.   else
  481.     the_rep[i++] = character;
  482.  
  483.   the_rep[i] = '\0';
  484.  
  485.   return (the_rep);
  486. }
  487.  
  488.  
  489. /* **************************************************************** */
  490. /*                                    */
  491. /*            Functions Static To This File            */
  492. /*                                    */
  493. /* **************************************************************** */
  494.  
  495. /* Amount of space allocated to INFO_PARSED_FILENAME via xmalloc (). */
  496. static int parsed_filename_size = 0;
  497.  
  498. /* Amount of space allocated to INFO_PARSED_NODENAME via xmalloc (). */
  499. static int parsed_nodename_size = 0;
  500.  
  501. static void save_string (), saven_string ();
  502.  
  503. /* Remember FILENAME in PARSED_FILENAME.  An empty FILENAME is translated
  504.    to a NULL pointer in PARSED_FILENAME. */
  505. static void
  506. save_filename (filename)
  507.      char *filename;
  508. {
  509.   save_string (filename, &info_parsed_filename, &parsed_filename_size);
  510. }
  511.  
  512. /* Just like save_filename (), but you pass the length of the string. */
  513. static void
  514. saven_filename (filename, len)
  515.      char *filename;
  516.      int len;
  517. {
  518.   saven_string (filename, len,
  519.         &info_parsed_filename, &parsed_filename_size);
  520. }
  521.  
  522. /* Remember NODENAME in PARSED_NODENAME.  An empty NODENAME is translated
  523.    to a NULL pointer in PARSED_NODENAME. */
  524. static void
  525. save_nodename (nodename)
  526.      char *nodename;
  527. {
  528.   save_string (nodename, &info_parsed_nodename, &parsed_nodename_size);
  529. }
  530.  
  531. /* Just like save_nodename (), but you pass the length of the string. */
  532. static void
  533. saven_nodename (nodename, len)
  534.      char *nodename;
  535.      int len;
  536. {
  537.   saven_string (nodename, len,
  538.         &info_parsed_nodename, &parsed_nodename_size);
  539. }
  540.  
  541. /* Remember STRING in STRING_P.  STRING_P should currently have STRING_SIZE_P
  542.    bytes allocated to it.  An empty STRING is translated to a NULL pointer
  543.    in STRING_P. */
  544. static void
  545. save_string (string, string_p, string_size_p)
  546.      char *string;
  547.      char **string_p;
  548.      int *string_size_p;
  549. {
  550.   if (!string || !*string)
  551.     {
  552.       if (*string_p)
  553.     free (*string_p);
  554.  
  555.       *string_p = (char *)NULL;
  556.       *string_size_p = 0;
  557.     }
  558.   else
  559.     {
  560.       if (strlen (string) >= *string_size_p)
  561.     *string_p = (char *)xrealloc
  562.       (*string_p, (*string_size_p = 1 + strlen (string)));
  563.  
  564.       strcpy (*string_p, string);
  565.     }
  566. }
  567.  
  568. /* Just like save_string (), but you also pass the length of STRING. */
  569. static void
  570. saven_string (string, len, string_p, string_size_p)
  571.      char *string;
  572.      int len;
  573.      char **string_p;
  574.      int *string_size_p;
  575. {
  576.   if (!string)
  577.     {
  578.       if (*string_p)
  579.     free (*string_p);
  580.  
  581.       *string_p = (char *)NULL;
  582.       *string_size_p = 0;
  583.     }
  584.   else
  585.     {
  586.       if (len >= *string_size_p)
  587.     *string_p = (char *)xrealloc (*string_p, (*string_size_p = 1 + len));
  588.  
  589.       strncpy (*string_p, string, len);
  590.       (*string_p)[len] = '\0';
  591.     }
  592. }
  593.  
  594. /* Return a pointer to the part of PATHNAME that simply defines the file. */
  595. char *
  596. filename_non_directory (pathname)
  597.      char *pathname;
  598. {
  599.   char *filename;
  600.  
  601.   filename = (char *)rindex (pathname, '/');
  602.  
  603.   if (filename)
  604.     filename++;
  605.   else
  606.     filename = pathname;
  607.  
  608.   return (filename);
  609. }
  610.  
  611. /* Return non-zero if NODE is one especially created by Info. */
  612. int
  613. internal_info_node_p (node)
  614.      NODE *node;
  615. {
  616.   if (node &&
  617.       (node->filename && !*node->filename) &&
  618.       !node->parent && node->nodename)
  619.     return (1);
  620.   else
  621.     return (0);
  622. }
  623.  
  624. /* Make NODE appear to be one especially created by Info. */
  625. void
  626. name_internal_node (node, name)
  627.      NODE *node;
  628.      char *name;
  629. {
  630.   if (!node)
  631.     return;
  632.  
  633.   node->filename = "";
  634.   node->parent = (char *)NULL;
  635.   node->nodename = name;
  636.   node->flags |= N_IsInternal;
  637. }
  638.  
  639. /* Return the window displaying NAME, the name of an internally created
  640.    Info window. */
  641. WINDOW *
  642. get_internal_info_window (name)
  643.      char *name;
  644. {
  645.   WINDOW *win = (WINDOW *)NULL;
  646.  
  647.   for (win = windows; win; win = win->next)
  648.     if (internal_info_node_p (win->node) &&
  649.     (strcmp (win->node->nodename, name) == 0))
  650.       break;
  651.  
  652.   return (win);
  653. }
  654.